1 module tinyredis_util.util; 2 3 import tinyredis : Redis, Response; 4 import std.datetime.systime : SysTime; 5 import std.experimental.logger; 6 7 /** 8 * Set a Redis variable. 9 * 10 * Params: 11 * redis = Database 12 * key = Variable name 13 * value = Variable value 14 */ 15 void set(T)(Redis redis, string key, T value) { 16 static if (is(T == SysTime)) { 17 long unixTime = value.toUnixTime!long; 18 redis.send("SET", key, unixTime); 19 } else { 20 redis.send("SET", key, value); 21 } 22 } 23 24 /** 25 * psetex works exactly like SETEX with the sole difference that the expire time is specified in milliseconds instead of seconds. 26 * 27 * Params: 28 * redis = Database 29 * key = Variable name 30 * milliseconds = Expire time 31 * value = Variable value 32 */ 33 void psetex(T)(Redis redis, string key, int milliseconds, T value) { 34 redis.send("PSETEX", key, milliseconds, value); 35 } 36 37 /** 38 * Set key to hold the string value and set key to timeout after a given number of seconds 39 * 40 * Params: 41 * redis = Database 42 * key = Variable name 43 * seconds = Expire time 44 * value = Variable value 45 */ 46 void setex(T)(Redis redis, string key, int seconds, T value) { 47 redis.send("SETEX", key, seconds, value); 48 } 49 50 /** 51 * Returns the value associated with field in the hash stored at key. 52 * 53 * Params: 54 * redis = Database 55 * key = Hash name 56 * field = Field name 57 */ 58 T hget(T)(Redis redis, string key, string field) { 59 static if (commonType!T) { 60 string reply = redis.send!string("HGET", key, field); 61 return conv!(T)(reply); 62 } else { 63 return redis.send!(T)("HGET", key, field); 64 } 65 } 66 67 /** 68 * Get a Redis variable 69 * 70 * Params: 71 * redis = Database 72 * key = Variable name 73 */ 74 T get(T)(Redis redis, string key) { 75 static if (commonType!T) { 76 string reply = redis.send!string("GET", key); 77 return conv!(T)(reply); 78 } else { 79 return redis.send!(T)("GET", key); 80 } 81 } 82 83 unittest { 84 Redis redis = new Redis(); 85 redis.send("select", 1); 86 redis.send("flushdb"); 87 redis.send("HMSET", "hh", "a", 10, "b", 11); 88 redis.send("HSET", "hh", "c", "12"); 89 int h0 = redis.hget!int("hh", "a"); 90 assert(h0 == 10); 91 92 string h1 = redis.hget!string("hh", "c"); 93 assert(h1 == "12"); 94 string h2 = redis.hget!string("hh", "b"); 95 assert(h2 == "11"); 96 } 97 98 template commonType(T) { 99 enum commonType = (is(T == bool) || is(T == float) || is(T == double) || is(T == short) || is(T == int) 100 || is(T == long) || is(T == uint) || is(T == ulong) || is(T == string) || is(T == SysTime)); 101 } 102 103 /** 104 * Convert a string into T type. 105 * 106 * 107 */ 108 T conv(T)(string input) if (commonType!T) { 109 import std.conv : to; 110 import std.string : isNumeric; 111 import std.datetime : DateTime; 112 113 static if (is(T == double) || (is(T == float))) { 114 if (input.isNumeric) { 115 return input.to!(T); 116 } else { 117 return input == "true" ? 1. : 0.; 118 } 119 } else static if ((is(T == int)) || (is(T == long)) || (is(T == uint)) || (is(T == ulong))) { 120 if (input.isNumeric) { 121 return input.to!(double) 122 .to!(T); 123 } else { 124 return input == "true" ? 1 : 0; 125 } 126 } else static if (is(T == bool)) { 127 if (input.isNumeric) { 128 return input.to!(double) != 0.; 129 } else { 130 return input == "true" || input == "t"; 131 } 132 } else static if (is(T == string)) { 133 return input; 134 } else static if (is(T == SysTime)) { 135 if (input.isNumeric) { 136 long unixTime = input.to!(double) 137 .to!long; 138 return SysTime.fromUnixTime(unixTime); 139 } else { 140 warning("empty datatime"); 141 return SysTime(DateTime(1970, 1, 1, 1, 1, 1)); 142 } 143 } else { 144 assert(false); 145 } 146 } 147 148 @("getdouble") 149 unittest { 150 import std.datetime : DateTime; 151 152 auto redis = new Redis("localhost", 6379); 153 redis.send("SELECT", 1); 154 redis.send("FLUSHDB"); 155 156 redis.send("SET", "delete_me", 3.14); 157 redis.send("SET", "delete:me", 3.15); 158 assert(redis.get!string("delete_me") == "3.14"); 159 assert(redis.get!int("delete_me") == 3); 160 assert(redis.get!double("delete_me") == 3.14); 161 assert(redis.get!bool("delete_me")); 162 163 assert(redis.get!string("delete:me") == "3.15"); 164 assert(redis.get!int("delete:me") == 3); 165 assert(redis.get!double("delete:me") == 3.15); 166 assert(redis.get!bool("delete:me")); 167 168 redis.send("SET", "delete_me", 0.0); 169 assert(redis.get!string("delete_me") == "0"); 170 assert(redis.get!int("delete_me") == 0); 171 assert(redis.get!double("delete_me") == 0.); 172 assert(!redis.get!bool("delete_me")); 173 174 redis.send("DEL", "delete_me"); 175 assert(redis.get!double("delete_me") == 0.); 176 177 redis.send("SET", "not_a_num", double.nan); 178 179 import std.math : isNaN; 180 181 assert(redis.get!double("not_a_num").isNaN); 182 183 enum UT = 1_552_320_073; 184 redis.send("SET", "ut", UT); 185 auto expected = SysTime(DateTime(2019, 3, 11, 17, 01, 13)); 186 187 assert(redis.get!SysTime("ut") == expected); 188 } 189 190 @("getint") 191 unittest { 192 auto redis = new Redis("localhost", 6379); 193 redis.send("SELECT", 1); 194 redis.send("FLUSHDB"); 195 196 redis.send("SET", "delete_me", 42); 197 assert(redis.get!string("delete_me") == "42"); 198 assert(redis.get!int("delete_me") == 42); 199 assert(redis.get!uint("delete_me") == 42); 200 assert(redis.get!long("delete_me") == 42L); 201 assert(redis.get!ulong("delete_me") == 42uL); 202 assert(redis.get!size_t("delete_me") == 42uL); 203 assert(redis.get!double("delete_me") == 42.); 204 assert(redis.get!bool("delete_me")); 205 206 redis.send("SET", "delete_me", 0); 207 assert(redis.get!string("delete_me") == "0"); 208 assert(redis.get!int("delete_me") == 0); 209 assert(redis.get!long("delete_me") == 0L); 210 assert(redis.get!double("delete_me") == 0.); 211 assert(!redis.get!bool("delete_me")); 212 213 redis.send("SET", "delete_me", -42); 214 assert(redis.get!string("delete_me") == "-42"); 215 assert(redis.get!int("delete_me") == -42); 216 //redis.get!uint("delete_me") == 42); overflow 217 assert(redis.get!long("delete_me") == -42L); 218 assert(redis.get!double("delete_me") == -42.); 219 assert(redis.get!bool("delete_me")); 220 } 221 222 @("getnull") 223 unittest { 224 auto redis = new Redis("localhost", 6379); 225 redis.send("SELECT", 1); 226 redis.send("FLUSHDB"); 227 228 assert(redis.get!string("none") == ""); 229 assert(redis.get!int("none") == 0); 230 assert(redis.get!uint("none") == 0); 231 assert(redis.get!long("none") == 0L); 232 assert(redis.get!double("none") == 0.); 233 assert(!redis.get!bool("none")); 234 } 235 236 @("getuint") 237 unittest { 238 auto redis = new Redis("localhost", 6379); 239 redis.send("SELECT", 1); 240 redis.send("FLUSHDB"); 241 242 redis.send("SET", "my_uint", cast(uint)42); 243 assert(redis.get!string("my_uint") == "42"); 244 assert(redis.get!uint("my_uint") == 42); 245 assert(redis.get!int("my_uint") == 42); 246 assert(redis.get!long("my_uint") == 42L); 247 assert(redis.get!double("my_uint") == 42.); 248 assert(redis.get!bool("my_uint")); 249 250 redis.send("SET", "my_uint", cast(uint)0); 251 assert(redis.get!string("my_uint") == "0"); 252 assert(redis.get!int("my_uint") == 0); 253 assert(redis.get!long("my_uint") == 0L); 254 assert(redis.get!double("my_uint") == 0.); 255 assert(!redis.get!bool("my_uint")); 256 } 257 258 @("getlong") 259 unittest { 260 auto redis = new Redis("localhost", 6379); 261 262 redis.send("SELECT", 1); 263 redis.send("FLUSHDB"); 264 265 redis.send("SET", "delete_me", 42L); 266 assert(redis.get!string("delete_me") == "42"); 267 assert(redis.get!int("delete_me") == 42); 268 assert(redis.get!long("delete_me") == 42L); 269 assert(redis.get!double("delete_me") == 42.); 270 assert(redis.get!bool("delete_me")); 271 272 redis.send("SET", "delete_me", 0L); 273 assert(redis.get!string("delete_me") == "0"); 274 assert(redis.get!int("delete_me") == 0); 275 assert(redis.get!long("delete_me") == 0L); 276 assert(redis.get!double("delete_me") == 0.); 277 assert(!redis.get!bool("delete_me")); 278 } 279 280 @("getbool") 281 unittest { 282 Redis redis = new Redis("localhost", 6379); 283 redis.send("SELECT", 1); 284 redis.send("FLUSHDB"); 285 286 redis.send("SET", "delete_me", true); 287 assert(redis.get!string("delete_me") == "true"); 288 assert(redis.get!int("delete_me") == 1); 289 assert(redis.get!long("delete_me") == 1L); 290 assert(redis.get!double("delete_me") == 1.); 291 assert(redis.get!bool("delete_me")); 292 293 redis.send("SET", "delete_me", false); 294 assert(redis.get!string("delete_me") == "false"); 295 assert(redis.get!int("delete_me") == 0); 296 assert(redis.get!long("delete_me") == 0L); 297 assert(redis.get!double("delete_me") == 0.); 298 assert(!redis.get!bool("delete_me")); 299 300 redis.send("SET", "bool_as_string", "true"); 301 assert(redis.get!bool("bool_as_string")); 302 redis.send("SET", "bool_as_string", "t"); 303 assert(redis.get!bool("bool_as_string")); 304 redis.send("SET", "bool_as_string", "f"); 305 assert(!redis.get!bool("bool_as_string")); 306 } 307 308 @("getstring") 309 unittest { 310 auto redis = new Redis("localhost", 6379); 311 redis.send("SELECT", 1); 312 redis.send("FLUSHDB"); 313 314 redis.send("SET", "delete_me", "true"); 315 assert(redis.get!string("delete_me") == "true"); 316 assert(redis.get!int("delete_me") == 1); 317 assert(redis.get!long("delete_me") == 1L); 318 assert(redis.get!double("delete_me") == 1.); 319 assert(redis.get!bool("delete_me")); 320 321 redis.send("SET", "delete_me", "false"); 322 assert(redis.get!string("delete_me") == "false"); 323 assert(redis.get!int("delete_me") == 0); 324 assert(redis.get!long("delete_me") == 0L); 325 assert(redis.get!double("delete_me") == 0.); 326 assert(!redis.get!bool("delete_me")); 327 328 redis.send("SET", "delete_me", "cul"); 329 assert(redis.get!string("delete_me") == "cul"); 330 assert(redis.get!int("delete_me") == 0); 331 assert(redis.get!long("delete_me") == 0L); 332 assert(redis.get!double("delete_me") == 0.); 333 assert(!redis.get!bool("delete_me")); 334 335 redis.send("SET", "delete_me", "42"); 336 assert(redis.get!string("delete_me") == "42"); 337 assert(redis.get!int("delete_me") == 42); 338 assert(redis.get!long("delete_me") == 42L); 339 assert(redis.get!double("delete_me") == 42.); 340 assert(redis.get!bool("delete_me")); 341 342 redis.send("SET", "delete_me", "3.14"); 343 assert(redis.get!string("delete_me") == "3.14"); 344 assert(redis.get!int("delete_me") == 3); 345 assert(redis.get!double("delete_me") == 3.14); 346 assert(redis.get!bool("delete_me")); 347 348 redis.send("SET", "delete_me", "3,14"); 349 assert(redis.get!string("delete_me") == "3,14"); 350 assert(redis.get!int("delete_me") == 0); 351 assert(redis.get!double("delete_me") == 0.); 352 assert(!redis.get!bool("delete_me")); 353 } 354 355 @("gettime") 356 unittest { 357 import std.datetime : DateTime; 358 import std.datetime.systime : SysTime; 359 360 auto redis = new Redis(); 361 redis.send("SELECT", 1); 362 redis.send("FLUSHDB"); 363 364 enum UT = 1_552_320_073; 365 redis.send("SET", "ut", UT); 366 auto expected = SysTime(DateTime(2019, 3, 11, 17, 01, 13)); 367 368 assert(redis.get!SysTime("ut") == expected); 369 370 redis.set!SysTime("ut1", expected); 371 assert(redis.get!long("ut1") == UT); 372 redis.send("SET", "ut", ""); 373 auto epoch = SysTime(DateTime(1970, 1, 1, 1, 1, 1)); 374 assert(redis.get!SysTime("ut") == epoch); 375 } 376 /** 377 * Returns the bit value at offset in the string value stored at key. 378 */ 379 bool getBit(Redis redis, string key, uint offset) { 380 return redis.send("GETBIT", key, offset).toBool; 381 } 382 383 /** 384 * Sets or clears the bit at offset in the string value stored at key. 385 * The bit is either set or cleared depending on value, which can be either 0 or 1. 386 * 387 * Params: 388 * redis = Database 389 * key = Key 390 * offset = Bit to set or reset 391 */ 392 void setBit(Redis redis, string key, uint offset, bool value) { 393 redis.send("SETBIT", key, offset, value ? 1 : 0); 394 } 395 396 /** 397 * Tests and sets (sets to 1) the bit. 398 * 399 * Internally use `SETBIT` function. 400 */ 401 bool bts(Redis redis, string key, uint bitnum) { 402 bool b = redis.getBit(key, bitnum); 403 redis.send("SETBIT", key, bitnum, 1); 404 return b; 405 } 406 407 /** 408 * Tests and resets (sets to 0) the bit. 409 * 410 * Internally use `SETBIT` function. 411 */ 412 bool btr(Redis redis, string key, uint bitnum) { 413 bool b = redis.getBit(key, bitnum); 414 redis.send("SETBIT", key, bitnum, 0); 415 return b; 416 } 417 418 /// 419 unittest { 420 auto redis = new Redis(); 421 redis.send("SELECT", 1); 422 redis.send("FLUSHDB"); 423 424 redis.setBit("bf", 3, true); 425 assert(redis.getBit("bf", 3)); 426 427 redis.setBit("bf", 0, true); 428 assert(redis.getBit("bf", 0)); 429 assert(!redis.getBit("bf", 1)); 430 assert(!redis.getBit("bf", 2)); 431 assert(redis.getBit("bf", 3)); 432 433 redis.btr("bf", 0); 434 assert(!redis.getBit("bf", 0)); 435 436 redis.btr("bf", 3); 437 assert(!redis.getBit("bf", 3)); 438 } 439 440 /** 441 * Safe convert Response 442 */ 443 T respTo(T)(Response response) { 444 import std.conv : to; 445 import std.string : isNumeric; 446 import std.datetime.systime : SysTime; 447 448 static if (is(T == double) || (is(T == float))) { 449 string reply = response.toString; 450 if (reply.isNumeric) { 451 return reply.to!(T); 452 } else { 453 return reply == "true" ? 1. : 0.; 454 } 455 } else static if ((is(T == int)) || (is(T == long)) || (is(T == uint)) || (is(T == ulong))) { 456 string reply = response.toString; 457 if (reply.isNumeric) { 458 return reply.to!(double) 459 .to!(T); 460 } else { 461 return reply == "true" ? 1 : 0; 462 } 463 } else static if (is(T == bool)) { 464 string reply = response.toString; 465 if (reply.isNumeric) { 466 return reply.to!(double) != 0.; 467 } else { 468 return reply == "true"; 469 } 470 } else { 471 return response.toString.to!T; 472 } 473 } 474 475 @("respTobool") 476 unittest { 477 Redis redis = new Redis("localhost", 6379); 478 redis.send("SELECT", 1); 479 redis.send("FLUSHDB"); 480 481 redis.send("SET", "delete_me", true); 482 assert(redis.send("GET", "delete_me").respTo!string == "true"); 483 assert(redis.send("GET", "delete_me").respTo!int == 1); 484 assert(redis.send("GET", "delete_me").respTo!long == 1L); 485 assert(redis.send("GET", "delete_me").respTo!double == 1.); 486 assert(redis.send("GET", "delete_me").respTo!bool); 487 488 redis.send("SET", "delete_me", false); 489 assert(redis.send("GET", "delete_me").respTo!string == "false"); 490 assert(redis.send("GET", "delete_me").respTo!int == 0); 491 assert(redis.send("GET", "delete_me").respTo!long == 0L); 492 assert(redis.send("GET", "delete_me").respTo!double == 0.); 493 assert(!redis.send("GET", "delete_me").respTo!bool); 494 } 495 496 @("respToString") 497 unittest { 498 auto redis = new Redis("localhost", 6379); 499 redis.send("SELECT", 1); 500 redis.send("FLUSHDB"); 501 502 redis.send("SET", "delete_me", "true"); 503 assert(redis.send("GET", "delete_me").respTo!string == "true"); 504 assert(redis.send("GET", "delete_me").respTo!int == 1); 505 assert(redis.send("GET", "delete_me").respTo!long == 1L); 506 assert(redis.send("GET", "delete_me").respTo!double == 1.); 507 assert(redis.send("GET", "delete_me").respTo!bool); 508 509 redis.send("SET", "delete_me", "false"); 510 assert(redis.send("GET", "delete_me").respTo!string == "false"); 511 assert(redis.send("GET", "delete_me").respTo!int == 0); 512 assert(redis.send("GET", "delete_me").respTo!long == 0L); 513 assert(redis.send("GET", "delete_me").respTo!double == 0.); 514 assert(!redis.send("GET", "delete_me").respTo!bool); 515 516 redis.send("SET", "delete_me", "cul"); 517 assert(redis.send("GET", "delete_me").respTo!string == "cul"); 518 assert(redis.send("GET", "delete_me").respTo!int == 0); 519 assert(redis.send("GET", "delete_me").respTo!long == 0L); 520 assert(redis.send("GET", "delete_me").respTo!double == 0.); 521 assert(!redis.send("GET", "delete_me").respTo!bool); 522 523 redis.send("SET", "delete_me", "42"); 524 assert(redis.send("GET", "delete_me").respTo!string == "42"); 525 assert(redis.send("GET", "delete_me").respTo!int == 42); 526 assert(redis.send("GET", "delete_me").respTo!long == 42L); 527 assert(redis.send("GET", "delete_me").respTo!double == 42.); 528 assert(redis.send("GET", "delete_me").respTo!bool); 529 530 redis.send("SET", "delete_me", "3.14"); 531 assert(redis.send("GET", "delete_me").respTo!string == "3.14"); 532 assert(redis.send("GET", "delete_me").respTo!int == 3); 533 assert(redis.send("GET", "delete_me").respTo!double == 3.14); 534 assert(redis.send("GET", "delete_me").respTo!bool); 535 536 redis.send("SET", "delete_me", "3,14"); 537 assert(redis.send("GET", "delete_me").respTo!string == "3,14"); 538 assert(redis.send("GET", "delete_me").respTo!int == 0); 539 assert(redis.send("GET", "delete_me").respTo!double == 0.); 540 assert(!redis.send("GET", "delete_me").respTo!bool); 541 } 542 543 @("respToDouble") 544 unittest { 545 auto redis = new Redis("localhost", 6379); 546 redis.send("SELECT", 1); 547 redis.send("FLUSHDB"); 548 549 redis.send("SET", "delete_me", 3.14); 550 assert(redis.send("GET", "delete_me").respTo!string == "3.14"); 551 assert(redis.send("GET", "delete_me").respTo!int == 3); 552 assert(redis.send("GET", "delete_me").respTo!double == 3.14); 553 assert(redis.send("GET", "delete_me").respTo!bool); 554 555 redis.send("SET", "delete:me", 3.15); 556 assert(redis.send("GET", "delete:me").respTo!string == "3.15"); 557 assert(redis.send("GET", "delete:me").respTo!int == 3); 558 assert(redis.send("GET", "delete:me").respTo!double == 3.15); 559 assert(redis.send("GET", "delete:me").respTo!bool); 560 561 redis.send("SET", "delete_me", 0.0); 562 assert(redis.send("GET", "delete_me").respTo!string == "0"); 563 assert(redis.send("GET", "delete_me").respTo!int == 0); 564 assert(redis.send("GET", "delete_me").respTo!double == 0.); 565 assert(!redis.send("GET", "delete_me").respTo!bool); 566 redis.send("DEL", "delete_me"); 567 assert(redis.send("GET", "delete_me").respTo!double == 0.); 568 569 redis.send("SET", "not_a_num", double.nan); 570 import std.math : isNaN; 571 572 assert(redis.send("GET", "not_a_num").respTo!double.isNaN); 573 } 574 575 /** 576 * Copy a structure into Redis variables. 577 * 578 * Examples: 579 * If the structure is: 580 * ``` 581 * struct Foo { 582 * int intParm; 583 * string stringParm; 584 * bool is60Hz 585 * } 586 * ``` 587 * 588 * Then 589 * ``` 590 * Foo foo; 591 * copyToRedis!Foo(foo, redis, "f:") 592 * ``` 593 * 594 * set these redis variables: 595 * 596 * $(LIST 597 * * f:int_parm 598 * * f:string_parm 599 * * f:is60_hz ATTENTION between letter and number does not add underscore 600 * ) 601 * 602 * Params: 603 * Params: 604 * source = Structure to copy 605 * target = Database in which to copy the structure 606 * prefix = Prefix to be added to the structure members 607 */ 608 void copyToRedis(T)(T source, Redis target, string prefix) { 609 import std.traits : hasMember, isBasicType, isSomeString, FieldNameTuple; 610 611 foreach (member; FieldNameTuple!T) { 612 auto value = __traits(getMember, source, member); 613 string name = member.camelCaseToSnake; 614 /* 615 * l'assegnazione: 616 * target[k ~ member] = value; 617 * e' eseguita a compile-time, se si omette static si ha un errore tentando di assegnare `let` ad un Parm 618 */ 619 static if (isBasicType!(typeof(value)) || isSomeString!(typeof(value))) { 620 target.set(prefix ~ name, value); 621 } 622 } 623 } 624 625 @("test_copy_pre") 626 unittest { 627 Redis redis = new Redis("localhost", 6379); 628 629 struct DummyData { 630 string condition; 631 string loggerName; 632 int noOfIteration; 633 double duration; 634 bool visible; 635 string[] lists; 636 } 637 638 DummyData t = { 639 condition: "aa", loggerName: "DD", visible: true, noOfIteration: 42, duration: 19.64, lists: ["a", "b"] 640 }; 641 642 t.copyToRedis!DummyData(redis, "cu_"); 643 assert(redis.get!string("cu_condition") == "aa"); 644 assert(redis.get!string("cu_logger_name") == "DD"); 645 assert(redis.get!bool("cu_visible")); 646 assert(redis.get!int("cu_no_of_iteration") == 42); 647 assert(redis.get!double("cu_duration") == 19.64); 648 // gli array sono ignorati 649 assert(redis.get!string("lists") == ""); 650 651 assert(redis.get!string("adasdadwerwerwerwer") == ""); 652 } 653 654 /** 655 * Copy the values of the Redis variables into a structure. 656 * 657 * The names of the redis variables to be used are the names of the members in snake_case with optional prefix. 658 * 659 * If T is a structure: 660 * ``` 661 * struct Foo { 662 * int fooName; 663 * } 664 * ``` 665 * 666 * Then `copyToStruct` copies the value of the variable `<prefix>foo_name` to foo.fooName 667 * 668 * 669 * Params: 670 * redis = Database from which to read the variables 671 * target = Structure that receives the values 672 * prefix = Prefix to be added to the variable names 673 */ 674 void copyToStruct(T)(Redis redis, ref T target, string prefix) { 675 import std.traits : hasMember; 676 677 foreach (member; __traits(allMembers, T)) { 678 auto m = __traits(getMember, target, member); 679 string name = member.camelCaseToSnake; 680 681 static if (is(typeof(m) == int)) { 682 __traits(getMember, target, member) = redis.get!int(prefix ~ name); 683 } else static if (is(typeof(m) == long)) { 684 __traits(getMember, target, member) = redis.get!long(prefix ~ name); 685 } else static if (is(typeof(m) == double)) { 686 __traits(getMember, target, member) = redis.get!double(prefix ~ name); 687 } else static if (is(typeof(m) == bool)) { 688 __traits(getMember, target, member) = redis.get!bool(prefix ~ name); 689 } else static if (is(typeof(m) == string)) { 690 __traits(getMember, target, member) = redis.get!string(prefix ~ name); 691 } 692 } 693 } 694 695 /// 696 @("copy2struct") 697 unittest { 698 Redis redis = new Redis("localhost", 6379); 699 redis.send("SELECT", 1); 700 redis.send("FLUSHDB"); 701 redis.set!string("cu_condition", "aa"); 702 redis.set!string("cu_logger_name", "DD"); 703 redis.set!bool("cu_visible", true); 704 redis.set!int("cu_no_of_iteration", 42); 705 redis.set!double("cu_duration", 19.64); 706 707 struct DummyData { 708 string condition; 709 string loggerName; 710 int noOfIteration; 711 double duration; 712 bool visible; 713 string[] lists; 714 } 715 716 DummyData dummy; 717 redis.copyToStruct(dummy, "cu_"); 718 assert(dummy.condition == "aa"); 719 assert(dummy.loggerName == "DD"); 720 assert(dummy.noOfIteration == 42); 721 assert(dummy.duration == 19.64); 722 assert(dummy.lists.length == 0); 723 } 724 725 /** 726 * Convert a lower camelcase string to snake case. 727 * We can't use regex to match at compile-time so we'll iterate through the string and convert it manually. 728 * 729 * See_Also: 730 * [see util.d](https://github.com/jjpatel/dhtags) 731 */ 732 string camelCaseToSnake(in string s) @safe pure { 733 import std.array : join; 734 import std.range : enumerate; 735 import std.algorithm : map; 736 import std.ascii : isUpper, isLower, isDigit; 737 import std.string : toLower; 738 import std.conv : to; 739 740 return s.enumerate.map!((t) { 741 if (isUpper(t.value)) { 742 if (t.index > 0 && (isLower(s[t.index - 1]) || isDigit(s[t.index - 1]) || (t.index < s.length - 1 && isLower(s[t.index + 1])))) { 743 return "_" ~ t.value.toLower.to!string; 744 } else { 745 return t.value.toLower.to!string; 746 } 747 } else { 748 return t.value.to!string; 749 } 750 }).join; 751 } 752 753 /// 754 @("snake") 755 unittest { 756 assert("ABCD".camelCaseToSnake == "abcd"); 757 assert("A0CD".camelCaseToSnake == "a0_cd"); 758 assert("aBcD".camelCaseToSnake == "a_bc_d"); 759 assert("aBcDE".camelCaseToSnake == "a_bc_de"); 760 assert("a0CDe".camelCaseToSnake == "a0_c_de"); 761 assert("abCDe".camelCaseToSnake == "ab_c_de"); 762 assert("aBc1".camelCaseToSnake == "a_bc1"); 763 assert("xABy".camelCaseToSnake == "x_a_by"); 764 assert("caccaPipiPuzzetta".camelCaseToSnake == "cacca_pipi_puzzetta"); 765 assert("vacuum0PThreshold".camelCaseToSnake == "vacuum0_p_threshold"); 766 assert("vacuum0PressThreshold".camelCaseToSnake == "vacuum0_press_threshold"); 767 assert("".camelCaseToSnake == ""); 768 769 assert("cop3pAvg".camelCaseToSnake == "cop3p_avg"); 770 assert("cop3p".camelCaseToSnake == "cop3p"); 771 assert("vSupply3pMeas".camelCaseToSnake == "v_supply3p_meas"); 772 assert("pDisAtTCondSp".camelCaseToSnake == "p_dis_at_t_cond_sp"); 773 assert("res8r1".camelCaseToSnake == "res8r1"); 774 assert("pid00run".camelCaseToSnake == "pid00run"); 775 }